home *** CD-ROM | disk | FTP | other *** search
/ Linux Cubed Series 2: Applications / Linux Cubed Series 2 - Applications.iso / misc / ispell-3.001 / ispell-3~ / ispell-3.1 / defmt.c < prev    next >
C/C++ Source or Header  |  1995-10-12  |  24KB  |  902 lines

  1. #ifndef lint
  2. static char Rcs_Id[] =
  3.     "$Id: defmt.c,v 1.41 1995/08/05 23:19:47 geoff Exp $";
  4. #endif
  5.  
  6. /*
  7.  * defmt.c - Handle formatter constructs, mostly by scanning over them.
  8.  *
  9.  * This code originally resided in ispell.c, but was moved here to keep
  10.  * file sizes smaller.
  11.  *
  12.  * Copyright (c), 1983, by Pace Willisson
  13.  *
  14.  * Copyright 1992, 1993, Geoff Kuenning, Granada Hills, CA
  15.  * All rights reserved.
  16.  *
  17.  * Redistribution and use in source and binary forms, with or without
  18.  * modification, are permitted provided that the following conditions
  19.  * are met:
  20.  *
  21.  * 1. Redistributions of source code must retain the above copyright
  22.  *    notice, this list of conditions and the following disclaimer.
  23.  * 2. Redistributions in binary form must reproduce the above copyright
  24.  *    notice, this list of conditions and the following disclaimer in the
  25.  *    documentation and/or other materials provided with the distribution.
  26.  * 3. All modifications to the source code must be clearly marked as
  27.  *    such.  Binary redistributions based on modified source code
  28.  *    must be clearly marked as modified versions in the documentation
  29.  *    and/or other materials provided with the distribution.
  30.  * 4. All advertising materials mentioning features or use of this software
  31.  *    must display the following acknowledgment:
  32.  *      This product includes software developed by Geoff Kuenning and
  33.  *      other unpaid contributors.
  34.  * 5. The name of Geoff Kuenning may not be used to endorse or promote
  35.  *    products derived from this software without specific prior
  36.  *    written permission.
  37.  *
  38.  * THIS SOFTWARE IS PROVIDED BY GEOFF KUENNING AND CONTRIBUTORS ``AS IS'' AND
  39.  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  40.  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  41.  * ARE DISCLAIMED.  IN NO EVENT SHALL GEOFF KUENNING OR CONTRIBUTORS BE LIABLE
  42.  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
  43.  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
  44.  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
  45.  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
  46.  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
  47.  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  48.  * SUCH DAMAGE.
  49.  *
  50.  * The TeX code is originally by Greg Schaffer, with many improvements from
  51.  * Ken Stevens.  The nroff code is primarily from Pace Willisson, although
  52.  * other people have improved it.
  53.  */
  54.  
  55. /*
  56.  * $Log: defmt.c,v $
  57.  * Revision 1.41  1995/08/05  23:19:47  geoff
  58.  * Get rid of an obsolete comment.  Add recognition of documentclass and
  59.  * usepackage for Latex2e support.
  60.  *
  61.  * Revision 1.40  1995/03/06  02:42:43  geoff
  62.  * Change TeX backslash processing so that it only assumes alpha
  63.  * characters and the commonly-used "@" character are part of macro
  64.  * names, and so that any other backslashed character (specifically
  65.  * dollar signs) is skipped.
  66.  *
  67.  * Revision 1.39  1995/01/08  23:23:54  geoff
  68.  * Fix typos in a couple of comments.
  69.  *
  70.  * Revision 1.38  1995/01/03  19:24:14  geoff
  71.  * Add code to handle the LaTeX \verb command.
  72.  *
  73.  * Revision 1.37  1994/12/27  23:08:54  geoff
  74.  * Fix a bug in TeX backslash processing that caused ispell to become
  75.  * confused when it encountered an optional argument to a
  76.  * double-backslash command.  Be a little smarter about scanning for
  77.  * curly-brace matches, so that we avoid missing a math-mode transition
  78.  * during the scan.
  79.  *
  80.  * Revision 1.36  1994/10/25  05:46:34  geoff
  81.  * Recognize a few more Latex commands: pagestyle, pagenumbering,
  82.  * setcounter, addtocounter, setlength, addtolength, settowidth.
  83.  *
  84.  * Revision 1.35  1994/10/18  04:03:19  geoff
  85.  * Add code to skip hex numbers if they're preceded by '0x'.
  86.  *
  87.  * Revision 1.34  1994/10/04  03:51:24  geoff
  88.  * Modify the parsing so that TeX commands are ignored even within
  89.  * comments, but do not affect the overall parsing state.  (This is
  90.  * slightly imperfect, in that some types of modality are ignored when
  91.  * comments are entered.  But it should solve nearly all the problems
  92.  * with commented-out TeX commands.)  This also fixes a couple of minor
  93.  * bugs with TeX deformatting.
  94.  *
  95.  * Revision 1.33  1994/10/03  17:06:07  geoff
  96.  * Remember to use contextoffset when reporting complete misses
  97.  *
  98.  * Revision 1.32  1994/08/31  05:58:41  geoff
  99.  * Report the offset-within-line correctly in -a mode even if the line is
  100.  * longer than BUFSIZ characters.
  101.  *
  102.  * Revision 1.31  1994/05/25  04:29:28  geoff
  103.  * If two boundary characters appear in a row, consider it the end of the
  104.  * word.
  105.  *
  106.  * Revision 1.30  1994/05/17  06:44:08  geoff
  107.  * Add the new argument to all calls to good and compoundgood.
  108.  *
  109.  * Revision 1.29  1994/03/16  06:30:41  geoff
  110.  * Don't lose track of math mode when an array environment is embedded.
  111.  *
  112.  * Revision 1.28  1994/03/15  05:31:57  geoff
  113.  * Add TeX_strncmp, which allows us to handle AMS-TeX constructs like
  114.  * \endroster without getting confused.
  115.  *
  116.  * Revision 1.27  1994/02/14  00:34:53  geoff
  117.  * Pass length arguments to correct().
  118.  *
  119.  * Revision 1.26  1994/01/25  07:11:25  geoff
  120.  * Get rid of all old RCS log lines in preparation for the 3.1 release.
  121.  *
  122.  */
  123.  
  124. #include <ctype.h>
  125. #include "config.h"
  126. #include "ispell.h"
  127. #include "proto.h"
  128. #include "msgs.h"
  129.  
  130. static char *    skiptoword P ((char * bufp));
  131. char *        skipoverword P ((char * bufp));
  132. void        checkline P ((FILE * ofile));
  133. static int    TeX_math_end P ((char ** bufp));
  134. static int    TeX_math_begin P ((char ** bufp));
  135. static int    TeX_LR_begin P ((char ** bufp));
  136. static int    TeX_LR_check P ((int begin_p, char ** bufp));
  137. static void    TeX_skip_args P ((char ** bufp));
  138. static int    TeX_math_check P ((int cont_char, char ** bufp));
  139. static void    TeX_skip_parens P ((char ** bufp));
  140. static void    TeX_open_paren P ((char ** bufp));
  141. static void    TeX_skip_check P ((char ** bufp));
  142. static int    TeX_strncmp P ((char * a, char * b, int n));
  143.  
  144. #define ISTEXTERM(c)   (((c) == TEXLEFTCURLY) || \
  145.             ((c) == TEXRIGHTCURLY) || \
  146.             ((c) == TEXLEFTSQUARE) || \
  147.             ((c) == TEXRIGHTSQUARE))
  148. #define ISMATHCH(c)    (((c) == TEXBACKSLASH) || \
  149.             ((c) == TEXDOLLAR) || \
  150.             ((c) == TEXPERCENT))
  151.  
  152. static int        TeX_comment = 0;
  153.  
  154. /*
  155.  * The following variables are used to save the parsing state when
  156.  * processing comments.  This allows comments to be parsed without
  157.  * affecting the overall nesting.
  158.  */
  159.  
  160. static int save_math_mode;
  161. static char save_LaTeX_Mode;
  162.  
  163. static char * skiptoword (bufp)        /* Skip to beginning of a word */
  164.     char *    bufp;
  165.     {
  166.  
  167.     while (*bufp
  168.       &&  ((!isstringch (bufp, 0)  &&  !iswordch (chartoichar (*bufp)))
  169.     ||  isboundarych (chartoichar (*bufp))
  170.     ||  (tflag  &&  (math_mode & 1)))
  171.       )
  172.     {
  173.     /* check paren necessity... */
  174.     if (tflag) /* TeX or LaTeX stuff */
  175.         {
  176.         /* Odd numbers mean we are in "math mode" */
  177.         /* Even numbers mean we are in LR or */
  178.         /* paragraph mode */
  179.         if (*bufp == TEXPERCENT)
  180.         {
  181.         if (!TeX_comment)
  182.             {
  183.             save_math_mode = math_mode;
  184.             save_LaTeX_Mode = LaTeX_Mode;
  185.             math_mode = 0;
  186.             LaTeX_Mode = 'P';
  187.             TeX_comment = 1;
  188.             }
  189.         }
  190.         else if (math_mode & 1)
  191.         {
  192.         if ((LaTeX_Mode == 'e'  &&  TeX_math_check('e', &bufp))
  193.           || (LaTeX_Mode == 'm'  &&  TeX_LR_check(1, &bufp)))
  194.             math_mode--;    /* end math mode */
  195.         else
  196.             {
  197.             while (*bufp  && !ISMATHCH(*bufp))
  198.             bufp++;
  199.             if (*bufp == 0)
  200.             break;
  201.             if (TeX_math_end(&bufp))
  202.             math_mode--;
  203.             }
  204.         if (math_mode < 0)
  205.             {
  206.             (void) fprintf (stderr,
  207.              DEFMT_C_TEX_MATH_ERROR);
  208.             math_mode = 0;
  209.             }
  210.         }
  211.         else
  212.         {
  213.         if (math_mode > 1
  214.           &&  *bufp == TEXRIGHTCURLY
  215.           &&  (math_mode < (math_mode & 127) * 128))
  216.             math_mode--;    /* re-enter math */
  217.         else if (LaTeX_Mode == 'm'
  218.             || (math_mode && (math_mode >= (math_mode & 127) * 128)
  219.           &&  (TeX_strncmp(bufp, "\\end", 4)
  220.             == 0)))
  221.             {
  222.             if (TeX_LR_check(0, &bufp))
  223.             math_mode--;
  224.             }
  225.         else if (LaTeX_Mode == 'b'  &&  TeX_math_check('b', &bufp))
  226.             {
  227.             /* continued begin */
  228.             math_mode++;
  229.             }
  230.         else if (LaTeX_Mode == 'r')
  231.             {
  232.             /* continued "reference" */
  233.             TeX_skip_parens(&bufp);
  234.             LaTeX_Mode = 'P';
  235.             }
  236.         else if (TeX_math_begin(&bufp))
  237.             /* checks references and */
  238.             /* skips \ commands */
  239.             math_mode++;
  240.         }
  241.         if (*bufp == 0)
  242.         break;
  243.         }
  244.     else            /* formatting escape sequences */
  245.         {
  246.         if (*bufp == NRBACKSLASH)
  247.         {
  248.         switch ( bufp[1] )
  249.             {
  250.             case 'f':
  251.             if(bufp[2] == NRLEFTPAREN)
  252.                 {
  253.                 /* font change: \f(XY */
  254.                 bufp += 5;
  255.                 }
  256.             else
  257.                 {
  258.                 /* ) */
  259.                 /* font change: \fX */
  260.                 bufp += 3;
  261.                 }
  262.             continue;
  263.             case 's':
  264.             /* size change */
  265.             bufp += 2;
  266.             if (*bufp == '+'  ||  *bufp == '-')
  267.                 bufp++;
  268.             /* This looks wierd 'cause we
  269.             ** assume *bufp is now a digit.
  270.             */
  271.             bufp++;
  272.             if (isdigit (*bufp))
  273.                 bufp++;
  274.             continue;
  275.             default:
  276.             if (bufp[1] == NRLEFTPAREN)
  277.                 {
  278.                 /* extended char set */
  279.                 /* escape:  \(XX */
  280.                 /* ) */
  281.                 bufp += 4;
  282.                 continue;
  283.                 }
  284.             else if (bufp[1] == NRSTAR)
  285.                 {
  286.                 if (bufp[2] == NRLEFTPAREN)
  287.                 bufp += 5;
  288.                 else
  289.                 bufp += 3;
  290.                 continue;
  291.                 }
  292.             break;
  293.             }
  294.         }
  295.         }
  296.     /*
  297.      * Skip hex numbers, but not if we're in non-terse askmode.
  298.      * (In that case, we'd lose sync if we skipped hex.)
  299.      */
  300.     if (*bufp == '0'
  301.       &&  (bufp[1] == 'x'  ||  bufp[1] == 'X')
  302.       &&  (terse  ||  !aflag))
  303.         {
  304.         bufp += 2;
  305.         while (isxdigit (*bufp))
  306.         bufp++;
  307.         }
  308.     else
  309.         bufp++;
  310.     }
  311.     if (*bufp == '\0')
  312.     {
  313.     if (TeX_comment)
  314.         {
  315.         math_mode = save_math_mode;
  316.         LaTeX_Mode = save_LaTeX_Mode;
  317.         TeX_comment = 0;
  318.         }
  319.     }
  320.     return bufp;
  321.     }
  322.  
  323. char * skipoverword (bufp)    /* Return pointer to end of a word */
  324.     register char *    bufp;    /* Start of word -- MUST BE A REAL START */
  325.     {
  326.     register char *    lastboundary;
  327.     register int    scharlen; /* Length of a string character */
  328.  
  329.     lastboundary = NULL;
  330.     for (  ;  ;  )
  331.     {
  332.     if (*bufp == '\0')
  333.         {
  334.         if (TeX_comment)
  335.         {
  336.         math_mode = save_math_mode;
  337.         LaTeX_Mode = save_LaTeX_Mode;
  338.         TeX_comment = 0;
  339.         }
  340.         break;
  341.         }
  342.     else if (l_isstringch(bufp, scharlen, 0))
  343.         {
  344.         bufp += scharlen;
  345.         lastboundary = NULL;
  346.         }
  347.     /*
  348.     ** Note that we get here if a character satisfies
  349.     ** isstringstart() but isn't in the string table;  this
  350.     ** allows string characters to start with word characters.
  351.     */
  352.     else if (iswordch (chartoichar (*bufp)))
  353.         {
  354.         bufp++;
  355.         lastboundary = NULL;
  356.         }
  357.     else if (isboundarych (chartoichar (*bufp)))
  358.         {
  359.         if (lastboundary == NULL)
  360.         lastboundary = bufp;
  361.         else if (lastboundary == bufp - 1)
  362.         break;            /* Double boundary -- end of word */
  363.         bufp++;
  364.         }
  365.     else
  366.         break;            /* End of the word */
  367.     }
  368.     /*
  369.     ** If the word ended in one or more boundary characters, 
  370.     ** the address of the first of these is in lastboundary, and it
  371.     ** is the end of the word.  Otherwise, bufp is the end.
  372.     */
  373.     return (lastboundary != NULL) ? lastboundary : bufp;
  374.     }
  375.  
  376. void checkline (ofile)
  377.     FILE *        ofile;
  378.     {
  379.     register char *    p;
  380.     register char *    endp;
  381.     int            hadlf;
  382.     register int    len;
  383.     register int    i;
  384.     int            ilen;
  385.  
  386.     currentchar = contextbufs[0];
  387.     len = strlen (contextbufs[0]) - 1;
  388.     hadlf = contextbufs[0][len] == '\n';
  389.     if (hadlf)
  390.     contextbufs[0][len] = 0;
  391.  
  392.     if (!tflag)
  393.     {
  394.     /* skip over .if */
  395.     if (*currentchar == NRDOT
  396.       &&  (strncmp (currentchar + 1, "if t", 4) == 0
  397.         ||  strncmp (currentchar + 1, "if n", 4) == 0))
  398.         {
  399.         copyout (¤tchar,5);
  400.         while (*currentchar
  401.           &&  myspace (chartoichar (*currentchar)))
  402.         copyout (¤tchar, 1);
  403.         }
  404.  
  405.     /* skip over .ds XX or .nr XX */
  406.     if (*currentchar == NRDOT
  407.       &&  (strncmp (currentchar + 1, "ds ", 3) == 0 
  408.         ||  strncmp (currentchar + 1, "de ", 3) == 0
  409.         ||  strncmp (currentchar + 1, "nr ", 3) == 0))
  410.         {
  411.         copyout (¤tchar, 4);
  412.         while (*currentchar
  413.           &&  myspace (chartoichar (*currentchar)))
  414.         copyout(¤tchar, 1);
  415.         while (*currentchar
  416.           &&  !myspace (chartoichar (*currentchar)))
  417.         copyout(¤tchar, 1);
  418.         if (*currentchar == 0)
  419.         {
  420.         if (!lflag  &&  (aflag  ||  hadlf))
  421.             (void) putc ('\n', ofile);
  422.         return;
  423.         }
  424.         }
  425.     }
  426.  
  427.  
  428.     /* if this is a formatter command, skip over it */
  429.     if (!tflag && *currentchar == NRDOT)
  430.     {
  431.     while (*currentchar  &&  !myspace (chartoichar (*currentchar)))
  432.         {
  433.         if (!aflag && !lflag)
  434.         (void) putc (*currentchar, ofile);
  435.         currentchar++;
  436.         }
  437.     if (*currentchar == 0)
  438.         {
  439.         if (!lflag  &&  (aflag  ||  hadlf))
  440.         (void) putc ('\n', ofile);
  441.         return;
  442.         }
  443.     }
  444.  
  445.     for (  ;  ;  )
  446.     {
  447.     p = skiptoword (currentchar);
  448.     if (p != currentchar)
  449.         copyout (¤tchar, p - currentchar);
  450.  
  451.     if (*currentchar == 0)
  452.         break;
  453.  
  454.     p = ctoken;
  455.     endp = skipoverword (currentchar);
  456.     while (currentchar < endp  &&  p < ctoken + sizeof ctoken - 1)
  457.         *p++ = *currentchar++;
  458.     *p = 0;
  459.     if (strtoichar (itoken, ctoken, INPUTWORDLEN * sizeof (ichar_t), 0))
  460.         (void) fprintf (stderr, WORD_TOO_LONG (ctoken));
  461.     ilen = icharlen (itoken);
  462.  
  463.     if (lflag)
  464.         {
  465.         if (ilen > minword
  466.           &&  !good (itoken, 0, 0, 0, 0)
  467.           &&  !cflag  &&  !compoundgood (itoken, 0))
  468.         (void) fprintf (ofile, "%s\n", ctoken);
  469.         }
  470.     else
  471.         {
  472.         if (aflag)
  473.         {
  474.         if (ilen <= minword)
  475.             {
  476.             /* matched because of minword */
  477.             if (!terse)
  478.             (void) fprintf (ofile, "*\n");
  479.             continue;
  480.             }
  481.         if (good (itoken, 0, 0, 0, 0))
  482.             {
  483.             if (hits[0].prefix == NULL
  484.               &&  hits[0].suffix == NULL)
  485.             {
  486.             /* perfect match */
  487.             if (!terse)
  488.                 (void) fprintf (ofile, "*\n");
  489.             }
  490.             else if (!terse)
  491.             {
  492.             /* matched because of root */
  493.             (void) fprintf (ofile, "+ %s\n",
  494.               hits[0].dictent->word);
  495.             }
  496.             }
  497.         else if (compoundgood (itoken, 0))
  498.             {
  499.             /* compound-word match */
  500.             if (!terse)
  501.             (void) fprintf (ofile, "-\n");
  502.             }
  503.         else
  504.             {
  505.             makepossibilities (itoken);
  506.             if (pcount)
  507.             {
  508.             /*
  509.             ** print &  or ?, ctoken, then
  510.             ** character offset, possibility
  511.             ** count, and the possibilities.
  512.             */
  513.             (void) fprintf (ofile, "%c %s %d %d",
  514.               easypossibilities ? '&' : '?',
  515.               ctoken,
  516.               easypossibilities,
  517.               (int) ((currentchar - contextbufs[0])
  518.                 - strlen (ctoken)) + contextoffset);
  519.             for (i = 0;  i < MAXPOSSIBLE;  i++)
  520.                 {
  521.                 if (possibilities[i][0] == 0)
  522.                 break;
  523.                 (void) fprintf (ofile, "%c %s",
  524.                   i ? ',' : ':', possibilities[i]);
  525.                 }
  526.             (void) fprintf (ofile, "\n");
  527.             }
  528.             else
  529.             {
  530.             /*
  531.             ** No possibilities found for word TOKEN
  532.             */
  533.             (void) fprintf (ofile, "# %s %d\n",
  534.               ctoken,
  535.               (int) ((currentchar - contextbufs[0])
  536.                 - strlen (ctoken)) + contextoffset);
  537.             }
  538.             }
  539.         }
  540.         else
  541.         {
  542.         if (!quit)
  543.            correct (ctoken, sizeof ctoken, itoken, sizeof itoken,
  544.              ¤tchar);
  545.         }
  546.         }
  547.     if (!aflag  &&  !lflag)
  548.        (void) fprintf (ofile, "%s", ctoken);
  549.     }
  550.  
  551.     if (!lflag  &&  (aflag  ||  hadlf))
  552.        (void) putc ('\n', ofile);
  553.    }
  554.  
  555. /* must check for \begin{mbox} or whatever makes new text region. */
  556. static int TeX_math_end (bufp)
  557.     char **    bufp;
  558.     {
  559.  
  560.     if (**bufp == TEXDOLLAR)
  561.     {
  562.     if ((*bufp)[1] == TEXDOLLAR)
  563.         (*bufp)++;
  564.     return 1;
  565.     }
  566.     else if (**bufp == TEXPERCENT)
  567.     {
  568.     if (!TeX_comment)
  569.         {
  570.         save_math_mode = math_mode;
  571.         save_LaTeX_Mode = LaTeX_Mode;
  572.         math_mode = 0;
  573.         LaTeX_Mode = 'P';
  574.         TeX_comment = 1;
  575.         }
  576.     return 0;
  577.     }
  578.     /* processing extended TeX command */
  579.     (*bufp)++;
  580.     if (**bufp == TEXRIGHTPAREN  ||  **bufp == TEXRIGHTSQUARE)
  581.     return 1;
  582.     if (TeX_LR_begin (bufp))    /* check for switch back to LR mode */
  583.     return 1;
  584.     if (TeX_strncmp (*bufp, "end", 3) == 0)
  585.     /* find environment that is ending */
  586.     return TeX_math_check ('e', bufp);
  587.     else
  588.     return 0;
  589.     }
  590.  
  591. static int TeX_math_begin (bufp)
  592.     char **    bufp;
  593.     {
  594.  
  595.     if (**bufp == TEXDOLLAR)
  596.     {
  597.     if ((*bufp)[1] == TEXDOLLAR)
  598.         (*bufp)++;
  599.     return 1;
  600.     }
  601.     while (**bufp == TEXBACKSLASH)
  602.     {
  603.     (*bufp)++; /* check for null char here? */
  604.     if (**bufp == TEXLEFTPAREN  ||  **bufp == TEXLEFTSQUARE)
  605.         return 1;
  606.     else if (!isalpha(**bufp)  &&  **bufp != '@')
  607.         {
  608.         (*bufp)++;
  609.         continue;
  610.         }
  611.     else if (TeX_strncmp (*bufp, "begin", 5) == 0)
  612.         {
  613.         if (TeX_math_check ('b', bufp))
  614.         return 1;
  615.         else
  616.         (*bufp)--;
  617.         }
  618.     else
  619.         {
  620.         TeX_skip_check (bufp);
  621.         return 0;
  622.         }
  623.     }
  624.       /*
  625.        * Ignore references for the tib (1) bibliography system, that
  626.        * is, text between a ``[.'' or ``<.'' and ``.]'' or ``.>''.
  627.        * We don't care whether they match, tib doesn't care either.
  628.        *
  629.        * A limitation is that the entire tib reference must be on one
  630.        * line, or we break down and check the remainder anyway.
  631.        */ 
  632.     if ((**bufp == TEXLEFTSQUARE  ||  **bufp == TEXLEFTANGLE)
  633.       &&  (*bufp)[1] == TEXDOT)
  634.     {
  635.     (*bufp)++;
  636.     while (**bufp)
  637.         {
  638.         if (*(*bufp)++ == TEXDOT
  639.           &&  (**bufp == TEXRIGHTSQUARE  ||  **bufp == TEXRIGHTANGLE))
  640.         return TeX_math_begin (bufp);
  641.         }
  642.     return 0;
  643.     }
  644.     else
  645.     return 0;
  646.     }
  647.  
  648. static int TeX_LR_begin (bufp)
  649.     char **    bufp;
  650.     {
  651.  
  652.     if ((TeX_strncmp (*bufp, "mbox", 4) == 0)
  653.       ||  (TeX_strncmp (*bufp, "makebox", 7) == 0)
  654.       ||  (TeX_strncmp (*bufp, "fbox", 4) == 0)
  655.       || (TeX_strncmp (*bufp, "framebox", 8) == 0))
  656.     math_mode += 2;
  657.     else if ((TeX_strncmp(*bufp, "parbox", 6) == 0)
  658.       || (TeX_strncmp(*bufp, "raisebox", 8) == 0))
  659.     {
  660.     math_mode += 2;
  661.     TeX_open_paren (bufp);
  662.     if (**bufp)
  663.         (*bufp)++;
  664.     else
  665.         LaTeX_Mode = 'r'; /* same as reference -- skip {} */
  666.     }
  667.     else if (TeX_strncmp(*bufp, "begin", 5) == 0)
  668.     return TeX_LR_check (1, bufp);    /* minipage */
  669.     else
  670.     return 0;
  671.  
  672.     /* skip tex command name and optional or width arguments. */
  673.     TeX_open_paren (bufp);
  674.     return 1;
  675.     }
  676.  
  677. static int TeX_LR_check (begin_p, bufp)
  678.     int        begin_p;
  679.     char **    bufp;
  680.     {
  681.  
  682.     TeX_open_paren (bufp);
  683.     if (**bufp == 0)    /* { */
  684.     {
  685.     LaTeX_Mode = 'm';
  686.     return 0;    /* remain in math mode until '}' encountered. */
  687.     }
  688.     else
  689.     LaTeX_Mode = 'P';
  690.     if (strncmp (++(*bufp), "minipage", 8) == 0)
  691.     {
  692.     TeX_skip_parens (bufp);
  693.     if (**bufp)
  694.         (*bufp)++;
  695.     if (begin_p)
  696.         {
  697.         TeX_skip_parens (bufp); /* now skip opt. args if on this line. */
  698.         math_mode += 2;
  699.         /* indicate minipage mode. */
  700.         math_mode += ((math_mode & 127) - 1) * 128;
  701.         }
  702.     else
  703.         {
  704.         math_mode -= (math_mode & 127) * 128;
  705.         if (math_mode < 0)
  706.         {
  707.         (void) fprintf (stderr, DEFMT_C_LR_MATH_ERROR);
  708.         math_mode = 1;
  709.         }
  710.         }
  711.     return 1;
  712.     }
  713.     (*bufp)--;
  714.     return 0;
  715.     }
  716.  
  717. /* Skips the begin{ARG}, and optionally up to two {PARAM}{PARAM}'s to
  718.  *  the begin if they are required.  However, Only skips if on this line.
  719.  */
  720. static void TeX_skip_args (bufp)
  721.     char **    bufp;
  722.     {
  723.     register int skip_cnt = 0; /* Max of 2. */
  724.  
  725.     if (strncmp(*bufp, "tabular", 7) == 0
  726.       ||  strncmp(*bufp, "minipage", 8) == 0)
  727.     skip_cnt++;
  728.     if (strncmp(*bufp, "tabular*", 8) == 0)
  729.     skip_cnt++;
  730.     TeX_skip_parens (bufp);    /* Skip to the end of the \begin{} parens */
  731.     if (**bufp)
  732.     (*bufp)++;
  733.     else
  734.     return;
  735.     if (skip_cnt--)
  736.     TeX_skip_parens (bufp);    /* skip 1st {PARAM}. */
  737.     else
  738.     return;
  739.     if (**bufp)
  740.     (*bufp)++;
  741.     else
  742.     return;
  743.     if (skip_cnt)
  744.     TeX_skip_parens (bufp);    /* skip to end of 2nd {PARAM}. */
  745.     }
  746.  
  747. static int TeX_math_check (cont_char, bufp)
  748.     int        cont_char;
  749.     char **    bufp;
  750.     {
  751.  
  752.     TeX_open_paren (bufp);
  753.     /* Check for end of line, continue later. */
  754.     if (**bufp == 0)
  755.     {
  756.     LaTeX_Mode = (char) cont_char;
  757.     return 0;
  758.     }
  759.     else
  760.     LaTeX_Mode = 'P';
  761.  
  762.     if (strncmp (++(*bufp), "equation", 8) == 0
  763.       ||  strncmp (*bufp, "eqnarray", 8) == 0
  764.       ||  strncmp (*bufp, "displaymath", 11) == 0
  765.       ||  strncmp (*bufp, "picture", 7) == 0
  766. #ifdef IGNOREBIB
  767.       ||  strncmp (*bufp, "thebibliography", 15) == 0
  768. #endif
  769.       ||  strncmp (*bufp, "math", 4) == 0)
  770.     {
  771.     (*bufp)--;
  772.     TeX_skip_parens (bufp);
  773.     return 1;
  774.     }
  775.     if (cont_char == 'b')
  776.     TeX_skip_args (bufp);
  777.     else
  778.     TeX_skip_parens (bufp);
  779.     return 0;
  780.     }
  781.  
  782. static void TeX_skip_parens (bufp)
  783.     char **    bufp;
  784.     {
  785.  
  786.     while (**bufp  &&  **bufp != TEXRIGHTCURLY  &&  **bufp != TEXDOLLAR)
  787.     (*bufp)++;
  788.     }
  789.  
  790. static void TeX_open_paren (bufp)
  791.     char **    bufp;
  792.     {
  793.     while (**bufp  &&  **bufp != TEXLEFTCURLY  &&  **bufp != TEXDOLLAR)
  794.     (*bufp)++;
  795.     }
  796.  
  797. static void TeX_skip_check (bufp)
  798.     char **    bufp;
  799.     {
  800.     int        skip_ch;
  801.  
  802.     if (TeX_strncmp(*bufp, "end", 3) == 0
  803.       ||  TeX_strncmp(*bufp, "vspace", 6) == 0
  804.       ||  TeX_strncmp(*bufp, "hspace", 6) == 0
  805.       ||  TeX_strncmp(*bufp, "cite", 4) == 0
  806.       ||  TeX_strncmp(*bufp, "ref", 3) == 0
  807.       ||  TeX_strncmp(*bufp, "parbox", 6) == 0
  808.       ||  TeX_strncmp(*bufp, "label", 5) == 0
  809.       ||  TeX_strncmp(*bufp, "input", 5) == 0
  810.       ||  TeX_strncmp(*bufp, "nocite", 6) == 0
  811.       ||  TeX_strncmp(*bufp, "include", 7) == 0
  812.       ||  TeX_strncmp(*bufp, "includeonly", 11) == 0
  813.       ||  TeX_strncmp(*bufp, "documentstyle", 13) == 0
  814.       ||  TeX_strncmp(*bufp, "documentclass", 13) == 0
  815.       ||  TeX_strncmp(*bufp, "usepackage", 10) == 0
  816.       ||  TeX_strncmp(*bufp, "pagestyle", 9) == 0
  817.       ||  TeX_strncmp(*bufp, "pagenumbering", 13) == 0
  818. #ifndef IGNOREBIB
  819.       ||  TeX_strncmp(*bufp, "bibliography", 12) == 0
  820.       ||  TeX_strncmp(*bufp, "bibitem", 7) == 0
  821. #endif
  822.       ||  TeX_strncmp(*bufp, "hyphenation", 11) == 0
  823.       ||  TeX_strncmp(*bufp, "pageref", 7) == 0)
  824.     {
  825.     TeX_skip_parens (bufp);
  826.     if (**bufp == 0)
  827.         LaTeX_Mode = 'r';
  828.     }
  829.     else if (TeX_strncmp(*bufp, "rule", 4) == 0        /* skip two args. */
  830.       ||  TeX_strncmp(*bufp, "setcounter", 10) == 0
  831.       ||  TeX_strncmp(*bufp, "addtocounter", 12) == 0
  832.       ||  TeX_strncmp(*bufp, "setlength", 9) == 0
  833.       ||  TeX_strncmp(*bufp, "addtolength", 11) == 0
  834.       ||  TeX_strncmp(*bufp, "settowidth", 10) == 0)
  835.     {
  836.     TeX_skip_parens (bufp);
  837.     if (**bufp == 0)    /* Only skips one {} if not on same line. */
  838.         LaTeX_Mode = 'r';
  839.     else            /* Skip second arg. */
  840.         {
  841.         (*bufp)++;
  842.         TeX_skip_parens (bufp);
  843.         if (**bufp == 0)
  844.         LaTeX_Mode = 'r';
  845.         }
  846.     }
  847.     else if (TeX_strncmp (*bufp, "verb", 4) == 0)
  848.     {
  849.     skip_ch = (*bufp)[4];
  850.     *bufp += 5;
  851.     while (**bufp != skip_ch  &&  **bufp != '\0')
  852.         (*bufp)++;
  853.     }
  854.     else
  855.     {
  856.     /* Optional tex arguments sometimes should and
  857.     ** sometimes shouldn't be checked
  858.     ** (eg \section [C Programming] {foo} vs
  859.     **     \rule [3em] {0.015in} {5em})
  860.     ** SO -- we'll always spell-check it rather than make a
  861.     ** full LaTeX parser.
  862.     */
  863.  
  864.     /* Must look at the space after the command. */
  865.     while (isalpha(**bufp)  ||  **bufp == '@')
  866.         (*bufp)++;
  867.     /*
  868.     ** Our caller expects to skip over a single character.  So we
  869.     ** need to back up by one.  Ugh.
  870.     */
  871.     (*bufp)--;
  872.     }
  873.     }
  874.  
  875. /*
  876.  * TeX_strncmp is like strncmp, except that it returns inequality if
  877.  * the following character of a is alphabetic.  We do not use
  878.  * iswordch here because TeX itself won't normally accept
  879.  * nonalphabetics (except maybe on ISO Latin-1 installations?  I'll
  880.  * have to look into that).  As a special hack, because LaTeX uses the
  881.  * @ sign so much, we'll also accept that character.
  882.  *
  883.  * Properly speaking, the @ sign should be settable in the hash file
  884.  * header, but I doubt that it varies, and I don't want to change the
  885.  * syntax of affix files right now.
  886.  */
  887. static int TeX_strncmp (a, b, n)
  888.     char *    a;        /* Strings to compare */
  889.     char *    b;        /* ... */
  890.     int        n;        /* Number of characters to compare */
  891.     {
  892.     int        cmpresult;    /* Result of calling strncmp */
  893.  
  894.     cmpresult = strncmp (a, b, n);
  895.     if (cmpresult == 0)
  896.     {
  897.     if (isascii (a[n])  &&  isalpha (a[n]))
  898.         return 1;        /* Force inequality if alpha follows */
  899.     }
  900.     return cmpresult;
  901.     }
  902.